文章内容:用 webpack 打包两个简单的模块,分析打包后的代码,也就是运行时代码。

# 代码准备

index.js

const sum = require("./sum");

console.log(sum(6, 9));
1
2
3

sum.js

module.exports = (a, b) => {
  return a + b;
};
1
2
3

webpack.config.js

const path = require("path");
module.exports = {
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "build"),
  },
  mode: "none",
};
// npx webpack --entry ./index.js --output-path build --mode=none
1
2
3
4
5
6
7
8
9

在控制台执行

npx webpack
1

在目录下输出 build/main.js

/******/ (() => {
  // webpackBootstrap
  /******/ var __webpack_modules__ = [
    ,
    /* 0 */ /* 1 */
    /***/ (module) => {
      module.exports = (a, b) => {
        return a + b;
      };

      /***/
    },
    /******/
  ];
  /************************************************************************/
  /******/ // The module cache
  /******/ var __webpack_module_cache__ = {};
  /******/
  /******/ // The require function
  /******/ function __webpack_require__(moduleId) {
    /******/ // Check if module is in cache
    /******/ var cachedModule = __webpack_module_cache__[moduleId];
    /******/ if (cachedModule !== undefined) {
      /******/ return cachedModule.exports;
      /******/
    }
    /******/ // Create a new module (and put it into the cache)
    /******/ var module = (__webpack_module_cache__[moduleId] = {
      /******/ // no module.id needed
      /******/ // no module.loaded needed
      /******/ exports: {},
      /******/
    });
    /******/
    /******/ // Execute the module function
    /******/ __webpack_modules__[moduleId](
      module,
      module.exports,
      __webpack_require__
    );
    /******/
    /******/ // Return the exports of the module
    /******/ return module.exports;
    /******/
  }
  /******/
  /************************************************************************/
  var __webpack_exports__ = {};
  // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
  (() => {
    const sum = __webpack_require__(1);

    console.log(sum(6, 9));
  })();

  /******/
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

接下来对 main.js 的代码进行分析

# 运行时代码分析

# 整体分析

main.js 整个是一个立即执行函数,首先先看一下整体

立即执行函数内包裹了五个部分的内容:

  • 1、2、4 是变量的声明与赋值,
  • 3 是函数的声明,
  • 5 又是一个立即执行函数,

这时还不知道这些变量和函数的作用是什么,可以带着疑问去调试代码;

接下来就进行断点调试

  • 将断点打在 5 这个立即执行函数内部第一行代码上
  • 然后打开 JavaScript Debug Terminal,执行 node main.js
  • 进入我们所打的断点位置
  • 然后单步调试,看代码的执行情况

通过调试运行时代码分析得出

  • 1 -> __webpack_modules__:是一个数组,存放所有加载到的模块。在此示例中,__webpack_modules__[0] 是空的,__webpack_modules__[1] 是 sum.js 模块;
  • 2 -> __webpack_module_cache__:是一个对象,对模块执行结果进行缓存,这样能够保证每个模块只被执行一次;
  • 3 -> __webpack_require__:是一个函数,作用是加载模块,如果模块第一次被加载,则通过 __webpack_modules__[moduleId] 匹配上对应的模块,并进行缓存;如果是已加载的模块,则直接从 __webpack_module_cache__[moduleId] 取;
  • 4 -> __webpack_exports__:是一个对象,存放导出的内容;
  • 5 -> IIFE:加载入口模块

# __webpack_require__具体做了什么?

webpack 的模块加载器是如何实现的,见注释

/******/ (() => {
  // webpackBootstrap
  /******/ var __webpack_modules__ = [
    ,
    /* 0 */ /* 1 */
    /***/ (module) => {
      module.exports = (a, b) => {
        return a + b;
      };

      /***/
    },
    /******/
  ];
  /************************************************************************/
  /******/ // The module cache
  /******/ var __webpack_module_cache__ = {};
  /******/
  /******/ // The require function
  /******/ function __webpack_require__(moduleId) {
    /******/ // (1)检查模块是否存在于缓存中
    /******/ var cachedModule = __webpack_module_cache__[moduleId];
    // (2)如果缓存存在,则直接返回缓存中 moduleId 所对应的 exports 对象
    /******/ if (cachedModule !== undefined) {
      /******/ return cachedModule.exports;
      /******/
    }
    /******/ // (3)如果缓存不存在,则创建一个 module 对象,用于初始化 module 信息,同时创建对应的缓存
    /******/ var module = (__webpack_module_cache__[moduleId] = {
      /******/ // no module.id needed
      /******/ // no module.loaded needed
      /******/ exports: {},
      /******/
    });
    /******/
    /******/ // (4)通过 moduleId 匹配到 __webpack_modules__ 存放的对应的模块,并传入 module, module.exports, __webpack_require__ 参数,执行模块
    /******/ __webpack_modules__[moduleId](
      module,
      module.exports,
      __webpack_require__
    );
    /******/
    /******/ // (5)返回 module.exports
    /******/ return module.exports;
    /******/
  }
  /******/
  /************************************************************************/
  var __webpack_exports__ = {};
  // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
  (() => {
    const sum = __webpack_require__(1);

    console.log(sum(6, 9));
  })();

  /******/
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# 如何理解 5 -> IIFE:加载入口函数

将 main.js 改造一下

/******/ (() => {
  // webpackBootstrap
  /******/ var __webpack_modules__ = [
    /* 0 */
    () => {
      const sum = __webpack_require__(1);

      console.log(sum(6, 9));
    },
    /* 1 */
    /***/ (module) => {
      module.exports = (a, b) => {
        return a + b;
      };

      /***/
    },
    /******/
  ];
  /************************************************************************/
  /******/ // The module cache
  /******/ var __webpack_module_cache__ = {};
  /******/
  /******/ // The require function
  /******/ function __webpack_require__(moduleId) {
    /******/ // Check if module is in cache
    /******/ var cachedModule = __webpack_module_cache__[moduleId];
    /******/ if (cachedModule !== undefined) {
      /******/ return cachedModule.exports;
      /******/
    }
    /******/ // Create a new module (and put it into the cache)
    /******/ var module = (__webpack_module_cache__[moduleId] = {
      /******/ // no module.id needed
      /******/ // no module.loaded needed
      /******/ exports: {},
      /******/
    });
    /******/
    /******/ // Execute the module function
    /******/ __webpack_modules__[moduleId](
      module,
      module.exports,
      __webpack_require__
    );
    /******/
    /******/ // Return the exports of the module
    /******/ return module.exports;
    /******/
  }
  /******/
  /************************************************************************/
  var __webpack_exports__ = {};
  // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
  __webpack_require__(0);

  /******/
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

可见 webpack 运行时代码最后是 __webpack_require__(0)

# 小结

通过对 webpack 运行时代码进行调试以及分析,如果不考虑缓存的情况,可用一句话说明其逻辑:加载入口模块,入口模块所依赖的其他模块,通过 __webpack_require__ 模块加载函数进行递归加载存放在 __webpack_modules__的对应模块。

上次更新: 2024年3月10日星期日上午10点19分